package com.vip.workflow.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.vip.common.response.ErrorCode;
import com.vip.common.response.Result;
import com.vip.common.response.Results;
import com.vip.workflow.dto.ProcessDTO;
import com.vip.workflow.service.IProcessService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipInputStream;

/**
 * 流程管理 Service实现类
 *
 * @author wgb
 * @date 2020/5/7 11:27
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ProcessServiceImpl implements IProcessService {

    private final RepositoryService repositoryService;

    @Override
    public Result<List<ProcessDTO>> getProcessList(Integer pageNo, Integer pageSize) {
        try {
            List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery().listPage((pageNo - 1) * pageSize, pageSize);
            int count = (int) repositoryService.createProcessDefinitionQuery().count();
            List<ProcessDTO> list = new ArrayList<>();
            for (ProcessDefinition processDefinition : processDefinitions) {
                list.add(new ProcessDTO(processDefinition));
            }
            return Results.newSuccessResult(list, "查询成功", count);
        } catch (Exception e) {
            log.error("获取模板列表出错", e);
        }
        return Results.newFailResult(ErrorCode.SYSTEM_ERROR, "查询失败");
    }

    @Override
    public Result<String> deployNewProcessByModelId(String modelId) {
        try {
            //获取模型
            Model modelData = repositoryService.getModel(modelId);
            byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());
            if (bytes == null) {
                return Results.newFailResult(ErrorCode.WORKFLOW_ERROR, "部署失败：型数据为空，请先设计流程并成功保存，再进行发布");
            }
            JsonNode modelNode = new ObjectMapper().readTree(bytes);
            BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            if (model.getProcesses().size() == 0) {
                return Results.newFailResult(ErrorCode.SYSTEM_ERROR, "数据模型不符要求，请至少设计一条主线流程。");
            }
            // 创建一个部署实例
            Deployment deployment = repositoryService.createDeployment().name(modelData.getName()).addBpmnModel(modelData.getKey() + ".bpmn20.xml", model).deploy();
            modelData.setDeploymentId(deployment.getId());
            repositoryService.saveModel(modelData);
            return Results.newSuccessResult(null, "流程部署成功");
        } catch (Exception e) {
            log.info("部署modelId:{}模型服务异常：{}", modelId, e);
        }
        return Results.newFailResult(ErrorCode.WORKFLOW_ERROR, "流程部署失败");
    }

    @Override
    public Result<String> processConvertToModel(String processId) {
        try {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processId).singleResult();
            InputStream bpmnStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
                  processDefinition.getResourceName());
            XMLInputFactory xif = XMLInputFactory.newInstance();
            InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
            XMLStreamReader xtr = xif.createXMLStreamReader(in);
            BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);
            BpmnJsonConverter converter = new BpmnJsonConverter();
            ObjectNode modelNode = converter.convertToJson(bpmnModel);
            Model modelData = repositoryService.newModel();
            modelData.setKey(processDefinition.getKey());
            modelData.setName(processDefinition.getResourceName());
            modelData.setCategory(processDefinition.getCategory());
            modelData.setDeploymentId(processDefinition.getDeploymentId());
            modelData.setVersion(Integer.parseInt(String.valueOf(repositoryService.createModelQuery().modelKey(modelData.getKey()).count() + 1)));
            ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
            modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
            modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, modelData.getVersion());
            modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());
            modelData.setMetaInfo(modelObjectNode.toString());
            repositoryService.saveModel(modelData);
            repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes("utf-8"));
            log.info("转换模型成功，模型ID={}", modelData.getId());
            return Results.newSuccessResult(modelData.getId(), "转换成功");
        } catch (Exception e) {
            log.error("流程转换为模板失败", e);
        }
        return Results.newFailResult(ErrorCode.WORKFLOW_ERROR, "转换失败");
    }

    @Override
    public Result<String> deleteProcessById(String id) {
        try {
            repositoryService.deleteDeployment(id, true);
            return Results.newSuccessResult(null, "删除成功");
        } catch (Exception e) {
            log.error("流程删除失败", e);
        }
        return Results.newFailResult(ErrorCode.WORKFLOW_ERROR, "删除失败");
    }

    @Override
    public Result<String> deleteMultiProcessById(List<String> ids) {
        try {
            for (String id : ids) {
                repositoryService.deleteDeployment(id, true);
            }
            return Results.newSuccessResult(null, "删除成功");
        } catch (Exception e) {
            log.error("流程删除失败", e);
        }
        return Results.newFailResult(ErrorCode.WORKFLOW_ERROR, "删除失败");
    }

    @Override
    public InputStream resourceReadByTypeAndId(String type, String id) {
        try {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();
            String resourceName = "";
            if ("image".equals(type)) {
                resourceName = processDefinition.getDiagramResourceName();
            } else if ("xml".equals(type)) {
                resourceName = processDefinition.getResourceName();
            }
            return repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
        } catch (Exception e) {
            log.error("获取指定流程的XML出错", e);
        }
        return null;
    }

    @Override
    public Result<String> uploadAndDeploy(MultipartFile file) {
        try {
            StringBuilder message = new StringBuilder();
            String fileName = file.getOriginalFilename();
            InputStream fileInputStream = file.getInputStream();
            Deployment deployment = null;
            String extension = FilenameUtils.getExtension(fileName);
            if ("zip".equals(extension) || "bar".equals(extension)) {
                ZipInputStream zip = new ZipInputStream(fileInputStream);
                deployment = repositoryService.createDeployment().addZipInputStream(zip).deploy();
            } else if ("png".equals(extension)) {
                deployment = repositoryService.createDeployment().addInputStream(fileName, fileInputStream).deploy();
            } else if (fileName.contains("bpmn20.xml")) {
                deployment = repositoryService.createDeployment().addInputStream(fileName, fileInputStream).deploy();
            } else if ("bpmn".equals(extension)) {
                // bpmn扩展名特殊处理，转换为bpmn20.xml
                String baseName = FilenameUtils.getBaseName(fileName);
                deployment = repositoryService.createDeployment().addInputStream(baseName + ".bpmn20.xml", fileInputStream).deploy();
            } else {
                message = new StringBuilder("不支持的文件类型：" + extension);
            }
            List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
            // 设置流程分类
            for (ProcessDefinition processDefinition : list) {
                repositoryService.setProcessDefinitionCategory(processDefinition.getId(), null);
                message.append("部署成功，流程ID=").append(processDefinition.getId());
            }
            if (list.size() == 0) {
                return Results.newFailResult(ErrorCode.WORKFLOW_ERROR, "部署失败：没有流程");
            }
            return Results.newSuccessResult(null, message.toString());
        } catch (Exception e) {
            log.error("上传部署失败", e);
        }
        return Results.newFailResult(ErrorCode.WORKFLOW_ERROR, "部署失败");
    }
}
